﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Timers;
using System.Threading;
using System.Windows.Forms;

namespace server
{
    public partial class Img : Form
    {
        private object o = new object();
        private const int N_CLIENT = 4;
        private const int N_THREAD = 8;
        private Image[] images = new Image[N_THREAD];

        private RectangleF[] rect = new RectangleF[N_THREAD];

        private bool esce = false;
        private activeWindow ac;
        private int top = 0, left = 0, height = 200, width = 200;
        private bool flag = true;
        private bool window = false;
        private bool val_window = true;
        private Cattura ct;
        private IntPtr hwnd;
        private TcpListener list;
        private TcpClient[] sVideo = new TcpClient[N_CLIENT];
        private bool[] free = new bool[N_CLIENT];
        private Stream[] str = new Stream[N_CLIENT];
        private Thread lThread, iThread;
        private Thread[] threadReceiver = new Thread[N_CLIENT];
        private const int DIM = 65000;
        private int act_client = 0;
        public delegate void imagedraw(Image img);
        private imagedraw imdrw;
        public delegate Image cloneimg();
        private cloneimg clone;
        private static ManualResetEvent[] resetEvents = new ManualResetEvent[N_THREAD];
        private const int CP_NOCLOSE_BUTTON = 0x200;
        public delegate void imagerectdraw(int i);
        public delegate void drawcursor(Point p);
        private drawcursor dcursor;

        

        public void drawim(Image img)
        {
            video.Image = img;
        }

        public void dr_curs(Point p)
        {
            Graphics g = Graphics.FromImage(video.Image);
            g.DrawImage(Properties.Resources.cursor_new, p);
            g.Dispose();
            video.Invalidate();
        }

        
        public Img(int t, int l, int w, int h, TcpListener lst)
        {
            InitializeComponent();
            top = t;
            left = l;
            width = w;
            height = h;
            list = lst;
            imdrw = new imagedraw(drawim);
            clone = new cloneimg(cloneimm);
            dcursor = new drawcursor(dr_curs);

           ct = new Cattura();
            ac = new activeWindow();
            for (int i = 0; i < N_CLIENT; i++)
                free[i] = true;
            lThread = new Thread(clientAccept);
            lThread.Start();
            iThread = new Thread(refresh);
            iThread.Start();

        }
        public Img(int t, int l, int w, int h, IntPtr hWnd, TcpListener lst)
        {
            InitializeComponent();
            window = true;
            top = t;
            left = l;
            width = w;
            height = h;
            hwnd = hWnd;
            list = lst;
            imdrw = new imagedraw(drawim);
            clone = new cloneimg(cloneimm);
            dcursor = new drawcursor(dr_curs);
            ct = new Cattura();
            ac = new activeWindow();
            for (int i = 0; i < N_CLIENT; i++)
                free[i] = true;
            lThread = new Thread(clientAccept);
            lThread.Start();
            iThread = new Thread(refresh);
            iThread.Start();
        }
        private void refresh()
        {
            bool first = true;
            while (!esce)
            {
                bool mouse_valid = false;
                if (flag && val_window)
                {
                    Image imm, imm2;
                    DateTime now = DateTime.Now;
                    Point p = Cursor.Position;
                    if (window)
                    {
                        p.X = p.X * video.Width / System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width;
                        p.Y = p.Y * video.Height / System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height;
                        Monitor.Enter(o);
                        if (ac.isOk(hwnd))
                        {
                            Rectangle rect;
                            rect = ct.getRect(hwnd);
                            rect.X = rect.X * video.Width / System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width;
                            rect.Y = rect.Y * video.Height / System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height;
                            rect.Width = rect.Width * video.Width / System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width;
                            rect.Height = rect.Height * video.Height / System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height;
                            if (p.X > rect.X && p.X < rect.X + rect.Width && p.Y > rect.Y && p.Y < rect.Y + rect.Height)
                            {
                                mouse_valid = true;
                                p.X = (p.X - rect.X) * video.Width / rect.Width;
                                p.Y = (p.Y - rect.Y) * video.Height / rect.Height;
                            }

                        }
                        else
                        {
                            val_window = false;
                            MessageBox.Show("Window closed");
                            imm = null;
                            int j = -1;
                            for (int i = 0; i < N_CLIENT; i++)
                                if (!free[i])
                                    str[i].Write(BitConverter.GetBytes(j), 0, 4);
                        }
                        Monitor.Exit(o);
                    }
                    else
                    {
                        Monitor.Enter(o);
                        if (p.X > left && p.X < width + left && p.Y > top && p.Y < height + top)
                        {
                            p.X = (p.X - left) * video.Width / width;
                            p.Y = (p.Y - top) * video.Height / height;
                            mouse_valid = true;
                        }
                        Monitor.Exit(o);
                    }
                    if (window)
                    {
                        Monitor.Enter(o);
                        if (ac.isOk(hwnd))
                        {
                            imm = M_Resize(ct.CaptureWindow(hwnd), video.Width, video.Height);
                        }
                        else
                        {
                            if(val_window)
                                MessageBox.Show("Window closed");
                            val_window = false;
                            imm = null;
                            int j = -1;
                            for (int i = 0; i < N_CLIENT; i++)
                                if (!free[i])
                                    str[i].Write(BitConverter.GetBytes(j), 0, 4);
                        }
                        Monitor.Exit(o);
                    }
                    else
                    {
                        Monitor.Enter(o);
                        Bitmap image = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                        System.Drawing.Graphics GFX = System.Drawing.Graphics.FromImage(image);
                        GFX.CopyFromScreen(System.Windows.Forms.Screen.PrimaryScreen.Bounds.X + left,
                                           System.Windows.Forms.Screen.PrimaryScreen.Bounds.Y + top,
                                           0, 0, new Size(width, height),
                                           System.Drawing.CopyPixelOperation.SourceCopy);
                        imm = M_Resize((Image)image, video.Width, video.Height);
                        GFX.Dispose();
                        Monitor.Exit(o);
                    }
                    if (val_window)
                    {
                        if (first)
                        {
                            Monitor.Enter(o);
                            Bitmap bmp = new Bitmap(512, 400);

                            Invoke(imdrw, (Image)bmp);
                            first = false;
                        }
                        else
                        {
                            try
                            {
                                Monitor.Enter(o);
                                imm2 = (Image)Invoke(clone);
                                Monitor.Exit(o);
                                byte[] tmp;
                                tmp = imageToByteArray(imm);
                                int altezza = imm.Height;
                                int lunghezza = imm.Width;
                                int[] v1 = BmpToBytes(new Bitmap(imm));
                                Monitor.Enter(o);
                                int[] v2 = BmpToBytes(new Bitmap(imm2));
                                Monitor.Exit(o);
                                for (int i = 0; i < N_THREAD; i++)
                                {
                                    resetEvents[i] = new ManualResetEvent(false);
                                    object[] array = new object[5];
                                    array[0] = (object)i;
                                    array[1] = (object)v1;
                                    array[2] = (object)v2;
                                    array[3] = (object)altezza;
                                    array[4] = (object)lunghezza;
                                    object ogg = (object)array;
                                    ThreadPool.QueueUserWorkItem(check, ogg);
                                }
                                WaitHandle.WaitAll(resetEvents);
                                Color color = Color.Yellow;
                                Color semiTransparentColor = Color.FromArgb(128, color.R, color.G, color.B);
                                SolidBrush sb = new SolidBrush(semiTransparentColor);
                                Invoke(imdrw, imm);
                                if (mouse_valid)
                                    Invoke(dcursor, p);
                                imm = (Image)Invoke(clone);
                                for (int i = 0; i < N_THREAD; i++)
                                {
                                    if (rect[i].Width != 0 && rect[i].Height != 0)
                                    {
                                        Monitor.Enter(o);
                                        Bitmap target = M_cut(imm, rect[i]);
                                        images[i] = (Image)target;
                                        Monitor.Exit(o);
                                    }
                                    else
                                    {
                                        images[i] = null;
                                    }
                                }
                                Monitor.Enter(o);
                                
                                for (int i = 0; i < N_CLIENT; i++)
                                    if (!free[i])
                                    {
                                        try
                                        {
                                            str[i].Write(BitConverter.GetBytes(-2), 0, 4);
                                            if(mouse_valid)
                                            {
                                                str[i].Write(BitConverter.GetBytes(p.X), 0, 4);
                                                str[i].Write(BitConverter.GetBytes(p.Y), 0, 4);
                                            }
                                            else
                                            {
                                                int temp = -1;
                                                str[i].Write(BitConverter.GetBytes(temp), 0, 4);
                                                str[i].Write(BitConverter.GetBytes(temp), 0, 4);
                                            }
                                        }
                                        catch
                                        {
                                            free[i] = true;
                                        }
                                        for (int j = 0; j < N_THREAD; j++)
                                        {
                                            if (images[j] != null)
                                            {
                                                tmp = imageToByteArray(images[j]);
                                                try
                                                {
                                                    str[i].Write(BitConverter.GetBytes(tmp.Length), 0, 4);
                                                    str[i].Write(BitConverter.GetBytes((int)rect[j].X), 0, 4);
                                                    str[i].Write(BitConverter.GetBytes((int)rect[j].Y), 0, 4);
                                                    str[i].Write(BitConverter.GetBytes((int)rect[j].Width), 0, 4);
                                                    str[i].Write(BitConverter.GetBytes((int)rect[j].Height), 0, 4);
                                                    str[i].Write(tmp, 0, tmp.Length);
                                                }
                                                catch
                                                {
                                                    free[i] = true;
                                                }
                                            }
                                        }
                                    }
                            }
                            catch
                            {
                                MessageBox.Show("Connections close!");
                                esce = true;
                                this.Close();
                            }
                            DateTime end = DateTime.Now;
                        }
                        try
                        {
                            Monitor.Exit(o);
                        }
                        catch
                        {
                            ;
                        }
                    }
                }
            }
        }

        private void check(object ogg)
        {
            object[] array = (object[])ogg;
            int i = (int)array[0];
            Int32[] v1 = (Int32[])array[1];
            Int32[] v2 = (Int32[])array[2];
            int altezza = (int)array[3];
            int lunghezza = (int)array[4];
            bool diversi = false;
            for (int l = altezza / N_THREAD * (i); l < altezza / N_THREAD * (i + 1) && !diversi; l++)
            {
                for (int j = 0; j < lunghezza && !diversi; j++)
                {
                    if (v1[j + l * lunghezza] != v2[j + l * lunghezza])
                    {
                        diversi = true;
                    }
                }
            }
            if (diversi)
            {
                rect[i] = new Rectangle(0, altezza / N_THREAD * (i), 512, 400 / N_THREAD);
            }
            else
            {
                rect[i] = new Rectangle(0, 0, 0, 0);
            }
            resetEvents[i].Set();
        }

        private Bitmap M_cut(Image img, RectangleF rect)
        {
            Bitmap bmpImage = new Bitmap(img);
            Bitmap bmpcrop = bmpImage.Clone(rect, bmpImage.PixelFormat);
            return bmpcrop;
        }


        private Image M_Resize(Image img, int resizedW, int resizedH)
        {
            Bitmap bmp = new Bitmap(resizedW, resizedH);
            Graphics graphic = Graphics.FromImage((Image)bmp);
            graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
            graphic.DrawImage(img, 0, 0, resizedW, resizedH);
            graphic.Dispose();
            return (Image)bmp;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            refresh();
        }

        public bool v_flag
        {
            set { flag = value; }
            get { return flag; }
        }

        private int[] BmpToBytes(Bitmap bmp)
        {
            BitmapData bData = bmp.LockBits(new Rectangle(new Point(), bmp.Size),
                ImageLockMode.ReadOnly,
                PixelFormat.Format32bppRgb);
            int byteCount = bData.Stride * (bmp.Height);
            int[] bytes = new int[byteCount / 4];
            Marshal.Copy(bData.Scan0, bytes, 0, byteCount / 4);
            bmp.UnlockBits(bData);
            return bytes;
        }

        
        private byte[] imageToByteArray(System.Drawing.Image imageIn)
        {
            try
            {
                MemoryStream ms = new MemoryStream();
                imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
                return ms.ToArray();
            }
            catch
            {
                return new byte[0];
            }
        }

        private void clientAccept()
        {
            try
            {
                while (true)
                {
                    TcpClient tmp = list.AcceptTcpClient();
                    Monitor.Enter(o);
                    if (act_client < N_CLIENT)
                    {
                        int i;
                        for (i = 0; !free[i]; i++) ;
                        byte[] vett = imageToByteArray(video.Image);
                        free[i] = false;
                        sVideo[i] = tmp;
                        try
                        {
                            str[i] = (sVideo[i]).GetStream();

                        }
                        catch
                        {
                            MessageBox.Show("Connection Error!");
                        }
                        act_client++;
                        Object obj = i;
                        threadReceiver[i] = new Thread(clientHandler);
                        threadReceiver[i].Start(obj);
                    }
                    else
                    {
                        tmp.GetStream().Write(Encoding.ASCII.GetBytes("Impossible".ToCharArray()), 0, "Impossible".Length);
                    }
                    Monitor.Exit(o);
                }
            }
            catch 
            {
                ;
            }
        }

        private Image cloneimm()
        {
            return (Image)video.Image.Clone();
        }

        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams myCp = base.CreateParams;
                myCp.ClassStyle = myCp.ClassStyle | CP_NOCLOSE_BUTTON;
                return myCp;
            }
        }
        private void clientHandler(object o)
        {
            int n = (int)o;
            bool esci = false;
            byte[] buf = new byte[4];
            BinaryReader stream = new BinaryReader(sVideo[n].GetStream());
            try
            {
                while (!esci)
                {
                    int len = sVideo[n].GetStream().Read(buf, 0, 4);
                    if (len != 0)
                    {
                        int l = BitConverter.ToInt32(buf, 0);
                        Monitor.Enter(o);
                        Image immagine = (Image)Invoke(clone);
                        byte[] array = imageToByteArray(immagine);
                        int lung = array.Length;
                        sVideo[n].GetStream().Write(BitConverter.GetBytes(lung), 0, 4);
                        sVideo[n].GetStream().Write(BitConverter.GetBytes(0), 0, 4);
                        sVideo[n].GetStream().Write(BitConverter.GetBytes(0), 0, 4);
                        sVideo[n].GetStream().Write(BitConverter.GetBytes(immagine.Width), 0, 4);
                        sVideo[n].GetStream().Write(BitConverter.GetBytes(immagine.Height), 0, 4);
                        sVideo[n].GetStream().Write(array, 0, lung);
                        Monitor.Exit(o);
                    }
                    else
                    {
                        esci = true;
                    }
                }
            }
            catch
            {
                ;
            }
            finally
            {
                act_client--;
                sVideo[n] = null;
                free[n] = true;
            }
        }

        public int i_top
        {
            set { top = value; }
        }
        public int i_width
        {
            set { width = value; }
        }
        public int i_height
        {
            set { height = value; }
        }
        public int i_left
        {
            set { left = value; }
        }
        public bool i_window
        {
            set { window = value; }
        }
        public IntPtr i_hwnd
        {
            set { hwnd = value; }
        }

        public bool valid_window
        {
            set { val_window = value; }
        }

        public bool set_exit
        {
            set { esce = value; }
        }

        public TcpListener getTcpListImg
        {
            get{return list;}
        }

        public Thread accClient
        {
            get { return lThread; }
        }
        public Thread[] getThreadReceiver
        {
            get { return threadReceiver; }
        }
        public TcpClient[] getImgTcpReceiver
        {
            get { return sVideo; }
        }
    }
}
